package com.taijihuabao.modules.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.util.CollectionUtils;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.github.pagehelper.PageHelper;
import com.taijihuabao.common.commonutils.DateFormatUtil;
import com.taijihuabao.common.exception.ServiceException;
import com.taijihuabao.common.jsonresult.ApplicationResponseCode;
import com.taijihuabao.common.commonutils.PageResult;
import com.taijihuabao.common.redisconfig.UserLoad;
import com.taijihuabao.modules.entity.CheckInEntity;
import com.taijihuabao.modules.entity.CheckInRuleEntity;
import com.taijihuabao.modules.enums.optenums.checkin.CheckInStatusEnum;
import com.taijihuabao.modules.enums.optenums.checkin.CheckInWorkEnum;
import com.taijihuabao.modules.mapper.CheckInMapper;
import com.taijihuabao.modules.mapper.CheckInRuleMapper;
import com.taijihuabao.modules.model.basedto.IdDTO;
import com.taijihuabao.modules.model.checkin.dto.*;
import com.taijihuabao.modules.model.checkin.vo.CheckInExportVO;
import com.taijihuabao.modules.model.checkin.vo.CheckInInfoVO;
import com.taijihuabao.modules.model.checkin.vo.CheckInListVO;
import com.taijihuabao.modules.model.checkin.vo.CheckInitVO;
import com.taijihuabao.modules.service.CheckInService;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 考勤管理
 *
 * @author changhui.tu
 */
@Service
public class CheckInServiceImpl implements CheckInService {

    private static final String EXPORT_FILEPATH = "D:\\Idea\\idea-workspace\\Taijihuabao\\work_environment\\";

    @Resource
    private CheckInMapper checkInMapper;
    @Resource
    private CheckInRuleMapper checkInRuleMapper;

    @Override
    public PageResult<CheckInListVO> selCheckInList(CheckInListDTO listDTO) {
        PageHelper.startPage(listDTO.getPage(), listDTO.getSize());
        List<CheckInListVO> checkInList = checkInMapper.selCheckInList(listDTO);
        return new PageResult<>(checkInList);
    }

    @Override
    public CheckInListVO selCheckInInfo(IdDTO idDTO) {
        if (StringUtils.isEmpty(idDTO.getId())) {
            throw new ServiceException(ApplicationResponseCode.ID_NOTNULL);
        }
        return checkInMapper.selCheckInInfo(idDTO.getId());
    }

    @Override
    public CheckInitVO initCheck(CheckInitDTO checkInitDTO) throws ParseException {
        CheckInEntity checkInEntity = new CheckInEntity();
        checkInEntity.setDateTime(DateFormatUtil.formatDate(checkInitDTO.getInitDate()));
        checkInEntity.setUid(UserLoad.getUser().getId());
        List<CheckInInfoVO> checkInfoList = new ArrayList<>();
        //查询上班
        checkInEntity.setClockType(Long.valueOf(CheckInWorkEnum.StartWork.getCode()));
        List<CheckInEntity> checkInStartWorkList = checkInMapper.selCheckInByUidAndClockType(checkInEntity);
        if (!CollectionUtils.isEmpty(checkInStartWorkList)) {
            CheckInInfoVO checkInInfoVO = new CheckInInfoVO();
            if (checkInStartWorkList.size() == 1) {
                checkInInfoVO.setIsRenewal(0);
            } else {
                checkInInfoVO.setIsRenewal(1);
            }
            for (CheckInEntity check : checkInStartWorkList) {
                if (check.getDeleteMark() == 1) {
                    BeanUtils.copyProperties(check, checkInInfoVO);
                    checkInfoList.add(checkInInfoVO);
                }
            }
        }
        //查询下班
        checkInEntity.setClockType(Long.valueOf(CheckInWorkEnum.OutWork.getCode()));
        List<CheckInEntity> checkInOutWorkList = checkInMapper.selCheckInByUidAndClockType(checkInEntity);
        if (!CollectionUtils.isEmpty(checkInOutWorkList)) {
            CheckInInfoVO checkInInfoVO = new CheckInInfoVO();
            if (checkInOutWorkList.size() == 1) {
                checkInInfoVO.setIsRenewal(0);
            } else {
                checkInInfoVO.setIsRenewal(1);
            }
            for (CheckInEntity check : checkInOutWorkList) {
                if (check.getDeleteMark() == 1) {
                    BeanUtils.copyProperties(check, checkInInfoVO);
                    checkInfoList.add(checkInInfoVO);
                }
            }
        }
        CheckInitVO checkInitVO = new CheckInitVO();
        checkInitVO.setCheckInList(checkInfoList);
        //判断上班卡是否截止
        CheckInRuleEntity checkInRuleEntity = checkInRuleMapper.selCheckInRule();
        boolean whetherStartWorkOut = DateFormatUtil.formatHourMin(DateFormatUtil.formatHourMin(checkInitDTO.getInitDate())).before(DateFormatUtil.formatHourMin(checkInRuleEntity.getCloseStartWork()));
        //判断打卡、更新状态
        List<CheckInEntity> checkInList = checkInMapper.selCheckInByUid(checkInEntity);
        if (checkInList.size() == 0 && whetherStartWorkOut) {
            checkInitVO.setCheckInCardStatus(1);
        } else {
            checkInitVO.setCheckInCardStatus(2);
        }
        return checkInitVO;
    }

    @Override
    public boolean checkInTimeVerify(CheckInTimeVerifyDTO checkInTimeVerifyDTO) {
        CheckInEntity checkInEntity = new CheckInEntity();
        checkInEntity.setDateTime(DateFormatUtil.formatDate(checkInTimeVerifyDTO.getReissueTime()));
        checkInEntity.setUid(UserLoad.getUser().getId());
        List<CheckInEntity> checkInList = checkInMapper.selCheckInByUid(checkInEntity);
        boolean startWork = true;
        boolean outWork = true;
        for (CheckInEntity check : checkInList) {
            String clockType = String.valueOf(check.getClockType());
            if (CheckInWorkEnum.StartWork.getCode().equals(clockType)) {
                startWork = false;
            }
            if (CheckInWorkEnum.OutWork.getCode().equals(clockType)) {
                outWork = false;
            }
        }
        return startWork || outWork;
    }

    @Override
    public void attendanceClock(CheckInCrFromDTO checkInCrFromDTO) throws ParseException {
        String dateTime = DateFormatUtil.formatDate(checkInCrFromDTO.getClockTime());
        CheckInEntity checkInEntity = new CheckInEntity();
        checkInEntity.setDateTime(dateTime);
        checkInEntity.setUid(UserLoad.getUser().getId());
        checkInEntity.setClockTime(checkInCrFromDTO.getClockTime());
        checkInEntity.setAddress(checkInCrFromDTO.getAddress());
        List<CheckInEntity> checkInList = checkInMapper.selCheckInByUid(checkInEntity);
        CheckInRuleEntity checkInRuleEntity = checkInRuleMapper.selCheckInRule();
        if (checkInRuleEntity == null) {
            throw new ServiceException(ApplicationResponseCode.CHECK_IN_PLEASE_INSERT);
        }
        boolean whetherStartWorkOut = DateFormatUtil.formatHourMin(DateFormatUtil.formatHourMin(checkInCrFromDTO.getClockTime())).before(DateFormatUtil.formatHourMin(checkInRuleEntity.getCloseStartWork()));
        if (checkInList.size() == 0 && whetherStartWorkOut) {
            checkInEntity.setClockType(Long.valueOf(CheckInWorkEnum.StartWork.getCode()));
            Date ruleTime = DateFormatUtil.formatHourMin(checkInRuleEntity.getStartWorkRule());
            Date clockTime = DateFormatUtil.formatHourMin(DateFormatUtil.formatHourMin(checkInCrFromDTO.getClockTime()));
            long diff = (clockTime.getTime() - ruleTime.getTime()) / 1000 / 60;
            if (diff > 0 && diff < checkInRuleEntity.getSeriousLateRule()) {
                //迟到
                checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.Late.getCode()));
            } else if (diff >= checkInRuleEntity.getSeriousLateRule() && diff < checkInRuleEntity.getAbsenteeismRule()) {
                //严重迟到
                checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.LateSeverity.getCode()));
            } else if (diff >= checkInRuleEntity.getAbsenteeismRule()) {
                //旷工
                checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.Absenteeism.getCode()));
            } else {
                //正常
                checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.Normal.getCode()));
            }
        } else {
            checkInEntity.setClockType(Long.valueOf(CheckInWorkEnum.OutWork.getCode()));
            if (CollectionUtils.isEmpty(checkInMapper.selCheckInByUidAndClockType(checkInEntity))) {
                Date ruleTime = DateFormatUtil.formatHourMin(checkInRuleEntity.getFinishWorkRule());
                Date clockTime = DateFormatUtil.formatHourMin(DateFormatUtil.formatHourMin(checkInCrFromDTO.getClockTime()));
                long diff = (ruleTime.getTime() - clockTime.getTime()) / 1000 / 60;
                if (diff > 0L) {
                    //早退
                    checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.LeaveEarly.getCode()));
                } else {
                    //正常
                    checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.Normal.getCode()));
                }
            } else {
                for (CheckInEntity check : checkInList) {
                    if (Long.valueOf(CheckInStatusEnum.LeaveEarly.getCode()).equals(check.getStateId())) {
                        throw new ServiceException(ApplicationResponseCode.CHECK_IN_LEAVE_EARLY);
                    }
                }
                checkInMapper.updCheckInClock(checkInEntity);
                checkInEntity.setStateId(Long.valueOf(CheckInStatusEnum.Normal.getCode()));
            }
        }
        checkInMapper.attendanceClock(checkInEntity);
    }

    @Override
    public void checkInExport(HttpServletResponse response, CheckInExportDTO checkInExportDTO) throws IOException {
        //转换属性
        CheckInEntity checkInEntity = new CheckInEntity();
        checkInEntity.setDateTime(checkInExportDTO.getDateTime());
        List<CheckInExportVO> checkList = new ArrayList<>();
        StringBuilder fileName = new StringBuilder("考勤记录");
        if (!CollectionUtils.isEmpty(checkInExportDTO.getUsers())) {
            for (int i = 0; i < checkInExportDTO.getUsers().size(); i++) {
                checkInEntity.setUid(checkInExportDTO.getUsers().get(i));
                //数据追加进新集合
                List<CheckInExportVO> exportList = checkInMapper.export(checkInEntity);
                if (CollectionUtils.isEmpty(exportList)) {
                    throw new ServiceException(ApplicationResponseCode.ID_NOTNULL);
                }
                if (!fileName.toString().contains(exportList.get(0).getUserName())) {
                    fileName.insert(0, exportList.get(0).getUserName());
                }
                checkList.addAll(exportList);
            }
        } else {
            List<CheckInExportVO> exportList = checkInMapper.export(checkInEntity);
            if (!CollectionUtils.isEmpty(exportList)) {
                checkList.addAll(exportList);
            }
        }
        List<CheckInExportVO> newCheckList = exportReplace(checkList);
        //头部策略
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        //内容策略
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        //设置水平居中
        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "utf-8"));
        // 这里需要设置不关闭流
        EasyExcel.write(response.getOutputStream(), CheckInExportVO.class).autoCloseStream(Boolean.FALSE)
                .registerWriteHandler(horizontalCellStyleStrategy).sheet("sheet1")
                .doWrite(newCheckList);
    }

    @Override
    public void exportForMonth(HttpServletResponse response) {
        String fileName = "考勤记录按月导出.xlsx";
        ExcelWriter excelWriter = null;
        try {
            //生成excel
            excelWriter = EasyExcel.write(fileName, CheckInExportVO.class).build();
            //从当前年份开始
            Calendar cal = Calendar.getInstance();
            int sheetNum = 0;
            int month = cal.get(Calendar.MONTH) + 1;
            int year = 2019;
            for (int j = cal.get(Calendar.YEAR); j > year; j--) {
                //循环生成月份工作表
                for (int i = month; i > 0; i--) {
                    sheetNum++;
                    //创建多个writeSheet工作表，注意:多次循环必须指定sheetNo且为有序，而且sheetName必须不一样
                    WriteSheet writeSheet = EasyExcel.writerSheet(sheetNum, j + "年" + i + "月").build();
                    List<CheckInExportVO> monthList = checkInMapper.exportForMonth(j, i);
                    List<CheckInExportVO> newMonthList = exportReplace(monthList);
                    excelWriter.write(newMonthList, writeSheet);
                }
                month = 12;
            }
        } catch (Exception e) {
            throw new ServiceException(ApplicationResponseCode.EXPORT_ERROR);
        } finally {
            try {
                if (excelWriter != null) {
                    excelWriter.finish();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        exportCommon(response, fileName);
    }

    @Override
    public void exportForWeek(HttpServletResponse response) {
        SimpleDateFormat formatMonthDay = new SimpleDateFormat("MM月dd日");
        String fileName = "考勤记录按周导出.xlsx";
        ExcelWriter excelWriter = null;
        try {
            //生成excel
            excelWriter = EasyExcel.write(fileName, CheckInExportVO.class).build();
            Calendar calendar = Calendar.getInstance();
            calendar.get(Calendar.DATE);
            int month = calendar.get(Calendar.MONTH);
            int sheetNum = 0;
            while (calendar.get(Calendar.MONTH) == month) {
                sheetNum++;
                if (calendar.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) {
                    //结束日期
                    String endTime = DateFormatUtil.formatDate(calendar.getTime());
                    String sheetEndTime = formatMonthDay.format(calendar.getTime());
                    //获取开始日期
                    calendar.add(Calendar.DATE, -6);
                    String beginTime = DateFormatUtil.formatDate(calendar.getTime());
                    String sheetBeginTime = formatMonthDay.format(calendar.getTime());
                    //创建多个writeSheet工作表，注意:多次循环必须指定sheetNo且为有序，而且sheetName必须不一样
                    WriteSheet writeSheet = EasyExcel.writerSheet(sheetNum, sheetBeginTime + "-" + sheetEndTime).build();
                    List<CheckInExportVO> weekList = checkInMapper.exportForWeek(beginTime, endTime);
                    List<CheckInExportVO> newWeekList = exportReplace(weekList);
                    excelWriter.write(newWeekList, writeSheet);
                }
                //到上周
                calendar.add(Calendar.DATE, -1);
            }
        } catch (Exception e) {
            throw new ServiceException(ApplicationResponseCode.EXPORT_ERROR);
        } finally {
            try {
                if (excelWriter != null) {
                    excelWriter.finish();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        exportCommon(response, fileName);
    }

    /**
     * 导出转换方法
     *
     * @param exportList 导出数据集合
     * @return 转换后集合
     */
    public List<CheckInExportVO> exportReplace(List<CheckInExportVO> exportList) {
        for (CheckInExportVO checkInExportVO : exportList) {
            if ("1".equals(checkInExportVO.getClockType())) {
                checkInExportVO.setClockType("上班");
            } else {
                checkInExportVO.setClockType("下班");
            }
            if ("1".equals(checkInExportVO.getDeleteMark())) {
                checkInExportVO.setDeleteMark("打卡成功");
            } else {
                checkInExportVO.setDeleteMark("打卡无效，此纪录已被更新!");
            }
        }
        return exportList;
    }

    /**
     * 导出方法
     *
     * @param response 响应文件
     * @param fileName 文件名
     */
    public void exportCommon(HttpServletResponse response, String fileName) {
        try (ServletOutputStream os = response.getOutputStream()) {
            //创建fastJson消息转换器,解决Content-Type: application/vnd.ms-excel
            List<MediaType> supportedMediaTypes = new ArrayList<>();
            supportedMediaTypes.add(MediaType.ALL);
            //设置文件类型
            response.setContentType("application/vnd.ms-excel");
            //指定字符集
            response.setCharacterEncoding("utf-8");
            // attachment是以附件的形式下载，inline是浏览器打开
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
            // 把二进制流放入到响应体中
            File file = new File(EXPORT_FILEPATH + fileName);
            //将文件内容写入字节数组
            byte[] bytes = FileUtils.readFileToByteArray(file);
            //输出
            os.write(bytes);
            //强制将缓存中的输出流（字节流，字符流等）强制输出
            os.flush();
            //删除本地文件
            file.delete();
        } catch (Exception e) {
            throw new ServiceException(ApplicationResponseCode.EXPORT_ERROR);
        }
    }

}
